﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using OilRigSumoWrestlers.Actions;
using OilRigSumoWrestlers.Controllables;
using RaptorEngine;
using RaptorEngine.Components;
using RaptorEngine.Components.CollidableComponent;
using RaptorEngine.Components.Drawable;

namespace OilRigSumoWrestlers.Levels
{
    public abstract class ALevel
    {

        protected int[,] _levelDesign = new int[GameConstant.LVL_LEVELSIZE, GameConstant.LVL_LEVELSIZE];
        public int[,] LevelDesign 
        {
            get { return _levelDesign; }
            set { _levelDesign = value; }
        }

        protected List<Entity> _levelTiles = new List<Entity>();
        protected Vector3 _player1Pos = Vector3.Zero;
        protected Vector3 _player2Pos = Vector3.Zero;
        protected BasicEffect _levelEffect;
        public BasicEffect LevelEffect { get { return _levelEffect; } }
        private BasicEffect _towerEffect;

        protected void GenerateLevelFromDesign()
        {
            _levelEffect = new BasicEffect(GameEngine.Instance.Render.GraphicsDevice);

            SetLevelLightning();
            SetTowerLighting();

            for (int x = 0; x < GameConstant.LVL_LEVELSIZE; x++)
            {
                for (int y = 0; y < GameConstant.LVL_LEVELSIZE; y++)
                {
                    //Get design number, Create correct entity, Add to level tiles
                    CreateEntityFromDesign(-x, y, _levelDesign[x, y]);
                }
            }
            SetSkyBox();
        }
        private void SetSkyBox()
        {
            Entity entity = new Entity();
            entity.Scale = 10;
            float center = (GameConstant.LVL_LEVELSIZE * GameConstant.LVL_TILESIZE)/2;
            entity.Position = new Vector3(center, 0, center);
            entity.AddComponent(new DrawableModel(entity, GameEngine.Instance.Load<Model>("Models/Cube/skybox"), _levelEffect));
            entity.AddComponent(new Collidable(entity, new Floor(0.98f), GameEngine.Instance.Load<Model>("Models/Cube/skybox"), false, (int)CollisionType.Ground));
            _levelTiles.Add(entity);
        }

        private void SetLevelLightning()
        {
            _levelEffect.LightingEnabled = true;
            _levelEffect.DirectionalLight0.DiffuseColor = new Vector3(1, 1, 1);
            _levelEffect.DirectionalLight0.Direction = new Vector3(0, -1, 0);
            _levelEffect.DirectionalLight0.SpecularColor = new Vector3(1, 0, 0);

            _levelEffect.DirectionalLight1.DiffuseColor = new Vector3(0.3f, 0.25f, 0);
            _levelEffect.DirectionalLight1.Direction = new Vector3(-0.4f, -1, 0.2f);
            _levelEffect.DirectionalLight1.SpecularColor = new Vector3(0, 0, 0);
        }

        private void SetTowerLighting()
        {
            _towerEffect = _levelEffect;
            _towerEffect.AmbientLightColor = new Vector3(1, 1, 1);
        }

        /// <summary>
        /// Creates the entity of the given index and type
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="eI"></param>
        private void CreateEntityFromDesign(int x, int y, int eI)
        {
            switch (eI)
            {
                case GameConstant.LVL_HOLE: // A hole
                    break;

                case GameConstant.LVL_FLOOR: // Regular Tile
                    Entity entity1 = new Entity();
                    entity1.Scale = 3;
                    entity1.Position = new Vector3(x * 2 * entity1.Scale, 2 * entity1.Scale, y * 2 * entity1.Scale);
                    entity1.AddComponent(new DrawableModel(entity1, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
                    entity1.AddComponent(new Collidable(entity1, new Floor(GameConstant.GROUND_FRICTION), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
                    _levelTiles.Add(entity1);
                    break;
                case GameConstant.LVL_OIL: // Oil tile
                    Entity entity2 = new Entity();
                    entity2.Scale = 3;
                    entity2.Position = new Vector3(x * 2 * entity2.Scale, 2 * entity2.Scale, y * 2 * entity2.Scale);
                    entity2.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.ToRadians(-180));
                    entity2.AddComponent(new DrawableModel(entity2, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
                    entity2.AddComponent(new Collidable(entity2, new Floor(GameConstant.OIL_FRICTION), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
                    _levelTiles.Add(entity2);
                    break;
                case GameConstant.LVL_TOWER: // Tower tile
                    Entity entity3 = new Entity();
                    entity3.Scale = 3;
                    entity3.Position = new Vector3(x * 2 * entity3.Scale, 2 * entity3.Scale, y * 2 * entity3.Scale);
                    entity3.AddComponent(new DrawableModel(entity3, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
					entity3.AddComponent(new Collidable(entity3, new Floor(GameConstant.GROUND_FRICTION), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
                    _levelTiles.Add(entity3);

                    //Tower
                    Entity entity32 = new Entity();
                    entity32.Scale = 3;
                    entity32.Position = new Vector3(x * 2 * entity32.Scale, 4 * entity32.Scale, y * 2 * entity32.Scale);
                    Model tower = GameEngine.Instance.Load<Model>("Models/Cube/Tower/tower3");
                    tower.Tag = "Tower";
                    entity32.AddComponent(new DrawableModel(entity32, tower, _towerEffect/*_levelEffect*/));
                    entity32.AddComponent(new Collidable(entity32, new BouncableCube(.6f), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
					entity32.AddComponent(new ParticleComp(entity32, ComponentType.Particle, "oilTower"));
					entity32.AddComponent(new ParticleSprayer(entity32, new Vector3(entity32.Position.X, entity32.Position.Y, entity32.Position.Z), new Vector3(0, 22, 0), 400));
                    _levelTiles.Add(entity32);
                    break;
                case GameConstant.LVL_BARREL: // Explosion tile
                    Entity entity4 = new Entity();
                    entity4.Scale = 3;
                    entity4.Position = new Vector3(x * 2 * entity4.Scale, 2 * entity4.Scale, y * 2 * entity4.Scale);
                    entity4.AddComponent(new DrawableModel(entity4, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
					entity4.AddComponent(new Collidable(entity4, new Floor(GameConstant.GROUND_FRICTION), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
					entity4.AddComponent(new ParticleComp(entity4, ComponentType.Particle, "explosion"));
					_levelTiles.Add(entity4);


                    Entity entity42 = new Entity();
                    entity42.Scale = 3;

                    entity42.Position = new Vector3(x * 2 * entity42.Scale, 3.55f * entity42.Scale, y * 2 * entity42.Scale);
                    entity42.Scale = 1.5f;

                    //entity42.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathHelper.ToRadians(-90));
                    entity42.AddComponent(new DrawableModel(entity42, GameEngine.Instance.Load<Model>("Models/barrel/barrel"), _levelEffect));
                    Collidable barrel = new Collidable(entity42, new BarrelAction(entity4), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground);
                    barrel.AddBoundingSphere("explosion", entity42.Position, Vector3.Zero, 10, (int)CollisionType.Ground, new ExplosionAction(0.15f));
                    barrel.BoundingVolumes["explosion"].IsActive = false;
                    entity42.AddComponent(barrel);
					entity42.AddComponent(new ParticleComp(entity42, ComponentType.Particle, "explosion"));
                    _levelTiles.Add(entity42);
                    break;

                case GameConstant.LVL_WALL: // Wall tile
                    Entity entity5 = new Entity();
                    entity5.Scale = 3;
                    entity5.Position = new Vector3(x * 2 * entity5.Scale, 4 * entity5.Scale, y * 2 * entity5.Scale);
                    entity5.AddComponent(new DrawableModel(entity5, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
                    entity5.AddComponent(new Collidable(entity5, new BouncableCube(.85f), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
                    _levelTiles.Add(entity5);

                    Entity entity52 = new Entity();
                    entity52.Scale = 3;
                    entity52.Position = new Vector3(x * 2 * entity52.Scale, 2 * entity52.Scale, y * 2 * entity52.Scale);
                    entity52.AddComponent(new DrawableModel(entity52, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
                    _levelTiles.Add(entity52);
                    break;

                case GameConstant.LVL_P1: // Player 1 position
                    Entity entity10 = new Entity();
                    entity10.Scale = 3;
                    entity10.Position = new Vector3(x * 2 * entity10.Scale, 2 * entity10.Scale, y * 2 * entity10.Scale);
                    entity10.AddComponent(new DrawableModel(entity10, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
					entity10.AddComponent(new Collidable(entity10, new Floor(GameConstant.GROUND_FRICTION), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
                    _levelTiles.Add(entity10);

                    _player1Pos = new Vector3(x * 2 * entity10.Scale, (2 * entity10.Scale)+5, y * 2 * entity10.Scale);
                    break;

                case GameConstant.LVL_P2: // Player 2 position
                    Entity entity20 = new Entity();
                    entity20.Scale = 3;
                    entity20.Position = new Vector3(x * 2 * entity20.Scale, 2 * entity20.Scale, y * 2 * entity20.Scale);
                    entity20.AddComponent(new DrawableModel(entity20, GameEngine.Instance.Load<Model>("Models/Cube/cube"), _levelEffect));
					entity20.AddComponent(new Collidable(entity20, new Floor(GameConstant.GROUND_FRICTION), GameEngine.Instance.Load<Model>("Models/Cube/cube"), false, (int)CollisionType.Ground));
                    _levelTiles.Add(entity20);

                    _player2Pos = new Vector3(x * 2 * entity20.Scale, (2 * entity20.Scale)+5, y * 2 * entity20.Scale);
                    break;
            }
        }

        protected void AddTile(Entity entity)
        {
            _levelTiles.Add(entity);
        }

        public List<Entity> GetAllTiles()
        {
            return _levelTiles;
        }

        /// <summary>
        /// Returns tile type for the position
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public int GetTypeAtPosition(Vector3 position)
        {
            //Move up left half a block
            Vector3 pos = position + new Vector3(-(GameConstant.LVL_TILESIZE / 2), 0, GameConstant.LVL_TILESIZE / 2);

            int x = (int)(pos.X / 6);
            int y = (int)(pos.Z / 6);

            // Flip from - to +
            x*=-1;

            if (x > -1 && x < GameConstant.LVL_LEVELSIZE && y > -1 && y < GameConstant.LVL_LEVELSIZE)
            {
                return _levelDesign[x, y];
            }

            return GameConstant.LVL_HOLE;
        }

        /// <summary>
        /// Returns tile type for the position
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public int GetTypeAtPosition(Vector2 position)
        {
            int x = (int)position.X;
            int y = (int)position.Y;

            //Move up left half a block
            if (x > -1 && x < GameConstant.LVL_LEVELSIZE && y > -1 && y < GameConstant.LVL_LEVELSIZE)
            {
                return _levelDesign[x, y];
            }

            return GameConstant.LVL_HOLE;
        }

        /// <summary>
        /// Returns index for tile at position
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public Vector2 GetIndexAtPosition(Vector3 position)
        {
            //Move up left half a block
            Vector3 pos = position + new Vector3(-(GameConstant.LVL_TILESIZE / 2), 0, GameConstant.LVL_TILESIZE / 2);

            int x = (int)(pos.X / 6);
            int y = (int)(pos.Z / 6);

            // Flip from - to +
            x *= -1;

            if (x > -1 && x < GameConstant.LVL_LEVELSIZE && y > -1 && y < GameConstant.LVL_LEVELSIZE)
            {
                return new Vector2(x,y);
            }

            return new Vector2(-1,-1);
        }

        /// <summary>
        ///  Returns world position for tile
        /// </summary>
        /// <param name="x">X position in tile matrix</param>
        /// <param name="y">Y Position in tile matrix</param>
        /// <returns></returns>
        public Vector3 GetPositionForTile(int x, int y)
        {
            //Move up left half a block
            if (x > -1 && x < GameConstant.LVL_LEVELSIZE && y > -1 && y < GameConstant.LVL_LEVELSIZE)
            {
                return new Vector3(-(x * GameConstant.LVL_TILESIZE), 0, (y * GameConstant.LVL_TILESIZE));
            }

            return new Vector3(-1,0,-1);
        }

        /// <summary>
        ///  Returns world position for tile
        /// </summary>
        /// <param name="position">Position in tile matrix</param>
        /// <returns></returns>
        public Vector3 GetPositionForTile(Vector2 position)
        {
            int x = (int)position.X;
            int y = (int)position.Y;
            //Move up left half a block
            if (x > -1 && x < GameConstant.LVL_LEVELSIZE && y > -1 && y < GameConstant.LVL_LEVELSIZE)
            {
                return new Vector3(-(x * GameConstant.LVL_TILESIZE), 0, (y * GameConstant.LVL_TILESIZE));
            }

            return new Vector3(-1, 0, -1);
        }

        public Vector3 GetPlayer1StartPosition()
        {
            return new Vector3(_player1Pos.X, _player1Pos.Y, _player1Pos.Z);
        }
        public Vector3 GetPlayer2StartPosition()
        {
            return new Vector3(_player2Pos.X, _player2Pos.Y, _player2Pos.Z); ;
        }
    }
}
